# coding=utf-8
# Chern-C
# Copyright (C) 2024 NJU-SAML
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from dataclasses import Field
from typing import List, Literal, Mapping, Optional, Dict, Union
from pydantic import BaseModel, ConfigDict, HttpUrl, ValidationError
import toml


class CargoFeatures(BaseModel):
    # 仅用于 nightly 版本的 feature
    features: List[str]


class Package(BaseModel):
    # 项目名称
    name: str
    # 版本号
    version: str
    # 开发作者
    authors: List[str]
    # Rust edition
    edition: Optional[str] = None
    # 最小化 Rust 版本
    rust_version: Optional[str] = None
    # 项目描述
    description: Optional[str] = None
    # 文档 URL
    documentation: Optional[HttpUrl] = None
    # README 文件路径
    readme: Optional[str] = None
    # 主页 URL
    homepage: Optional[HttpUrl] = None
    # 源代码仓库的 URL
    repository: Optional[HttpUrl] = None
    # 开源协议 License
    license: Optional[str] = None
    # License 文件路径
    license_file: Optional[str] = None
    # 项目关键词
    keywords: Optional[List[str]] = None
    # 项目分类
    categories: Optional[List[str]] = None
    # workspace 的路径
    workspace: Optional[str] = None
    # 构建脚本路径
    build: Optional[str] = None
    # 本地链接库的名称
    links: Optional[str] = None
    # 发布时排除的文件
    exclude: Optional[List[str]] = None
    # 发布时包含的文件
    include: Optional[List[str]] = None
    # 是否发布项目
    publish: Optional[bool] = None
    # 额外的配置信息
    metadata: Optional[Dict[str, Union[str, int, float, bool]]] = None
    # 默认可执行文件
    default_run: Optional[str] = None
    # 自动发现设置
    autobins: Optional[bool] = None
    autoexamples: Optional[bool] = None
    autotests: Optional[bool] = None
    autobenches: Optional[bool] = None
    # 依赖解析器设置
    resolver: Optional[str] = None


class Dependency(BaseModel):
    name: str
    version: str
    git: Optional[HttpUrl] = None
    branch: Optional[str] = None
    tag: Optional[str] = None
    rev: Optional[str] = None
    path: Optional[str] = None
    registry: Optional[str] = None

    class Config:
        extra = "forbid"


class PlatformTarget(BaseModel):
    # 平台特定依赖包
    target: Optional[Dict[str, Dict[str, Union[str, bool, List[str]]]]] = None


class Target(BaseModel):
    model_config = ConfigDict(alias_generator=lambda field_name: field_name.replace("_", "-"))
    name: str  # The name of the target.
    path: str = "src/lib.rs"  # The source file of the target.
    test: Optional[bool] = None  # Is tested by default.
    doctest: Optional[bool] = None  # Documentation examples are tested by default.
    bench: Optional[bool] = None  # Is benchmarked by default.
    doc: Optional[bool] = None  # Is documented by default.
    proc_macro: Optional[bool] = None  # Set to `true` for a proc-macro library.
    harness: Optional[bool] = None  # Use libtest harness.
    edition: Optional[str] = None  # The edition of the target.
    crate_type: Optional[List[Literal["bin", "lib", "dylib", "staticlib", "cdylib", "rlib", "proc-macro"]]] = (
        None  # The crate types to generate.
    )
    required_features: Optional[List[str]] = None  # Features required to build this target (N/A for lib).


class CargoConfig(BaseModel):
    # cargo-features 仅限于 nightly
    cargo_features: Optional[CargoFeatures] = None
    # package 元信息
    package: Optional[Package] = None
    # Library target 设置
    lib: Optional[Target] = None
    # Binary target 设置
    bin: Optional[List[Target]] = None
    # Example target 设置
    example: Optional[List[Dict[str, Union[str, bool]]]] = None
    # Test target 设置
    test: Optional[List[Dict[str, Union[str, bool]]]] = None
    # Benchmark target 设置
    bench: Optional[List[Dict[str, Union[str, bool]]]] = None
    # 依赖配置
    dependencies: Optional[Mapping[str, Union[str, Dependency]]] = None
    dev_dependencies: Optional[Dict[str, Union[str, Dependency]]] = None
    build_dependencies: Optional[Dict[str, Union[str, Dependency]]] = None
    # Target 配置
    target: Optional[PlatformTarget] = None
    # 项目状态信息，用于显示在 crates.io 等注册服务上
    badges: Optional[Dict[str, Dict[str, Union[str, bool]]]] = None
    # features 用于条件编译
    features: Optional[Dict[str, List[str]]] = None
    # 推荐的依赖覆盖方式
    patch: Optional[Dict[str, Dict[str, Union[str, bool, List[str]]]]] = None
    # 不推荐使用的依赖覆盖方式 (已废弃)
    replace: Optional[Dict[str, Dict[str, Union[str, bool, List[str]]]]] = None
    # 编译器设置和优化
    profile: Optional[Dict[str, Dict[str, Union[str, bool, int, float]]]] = None
    # 工作空间定义
    workspace: Optional[Dict[str, Union[str, List[str]]]] = None


def encode_cargo_config(cargo_config: CargoConfig) -> str:
    """
    将 Pydantic CargoConfig 实例编码为 TOML 格式的字符串。

    Args:
        cargo_config (CargoConfig): 包含配置的 Pydantic 模型实例。

    Returns:
        str: 编码后的 TOML 字符串。
    """
    # 将 Pydantic 实例转换为字典
    cargo_dict: Dict = cargo_config.model_dump(exclude_none=True)

    # 将字典转换为 TOML 格式的字符串
    toml_str = toml.dumps(cargo_dict)

    return toml_str


def write_cargo_config(cargo_config: CargoConfig, path: str):
    """
    将 Pydantic CargoConfig 实例写入到指定路径的 TOML 文件中。

    Args:
        cargo_config (CargoConfig): 包含配置的 Pydantic 模型实例。
        path (str): 要写入的文件路径。
    """
    # 将 Pydantic 实例转换为字典
    cargo_dict: Dict = cargo_config.model_dump(exclude_none=True)

    # 将字典转换为 TOML 格式并写入文件
    with open(path, "w", encoding="utf-8") as file:
        toml.dump(cargo_dict, file)


def decode_cargo_config(toml_str: str) -> CargoConfig:
    """
    将 TOML 格式的字符串解码为 Pydantic CargoConfig 实例。

    Args:
        toml_str (str): 要解码的 TOML 字符串。

    Returns:
        CargoConfig: 解码后的 Cargo 配置。

    Raises:
        ValidationError: 如果 TOML 字符串无效。
        TypeError: When a non-string is passed
        TomlDecodeError: Error while decoding toml
    """
    # 将 TOML 字符串解码为字典
    cargo_dict = toml.loads(toml_str)
    cargo_config = CargoConfig(**cargo_dict)
    return cargo_config


def read_cargo_config(path: str) -> CargoConfig:
    """
    从指定路径读取 TOML 文件，并转换为 CargoConfig 实例。

    Args:
        path (str): 要读取的文件路径。

    Returns:
        CargoConfig: 读取到的 Cargo 配置。

    Raises:
        ValidationError: 如果文件内容不符合 CargoConfig 模型格式。
    """
    # 读取 TOML 文件并将内容加载为字典
    with open(path, "r", encoding="utf-8") as file:
        cargo_dict = toml.load(file)

    # 尝试将字典加载为 CargoConfig 实例
    try:
        cargo_config = CargoConfig(**cargo_dict)
    except ValidationError as e:
        print("读取到的 TOML 数据无效:", e)
        raise

    return cargo_config
